package com.zzedu.framework.web.exception;

import com.alibaba.fastjson.JSON;
import com.zzedu.common.core.domain.AjaxResult;
import com.zzedu.common.exception.ErrorClientException;
import com.zzedu.common.exception.InvalidReqException;
import com.zzedu.common.exception.RateLimiterException;
import com.zzedu.common.exception.ServiceException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 全局异常处理器
 *
 * @author smarthome
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 权限校验异常（ajax请求返回json，redirect请求跳转页面）
     */
    @ExceptionHandler(AccessDeniedException.class)
    public Object handleAuthorizationException(AccessDeniedException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
        AjaxResult result = AjaxResult.error("无访问权限");
        result.put("resultCode", "FORBIDDEN");
        result.put("resultMessage","无访问权限");
        log.error("AccessDeniedException:{}", JSON.toJSONString(result));
        return result;

    }

    /**
     * 请求方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
                                                          HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
        AjaxResult result = AjaxResult.notFound("请求地址不正确,请检查URL", null);
        result.put("resultCode", "FAIL");
        result.put("resultMessage","请求地址不正确,请检查URL");
        log.error("handleHttpRequestMethodNotSupported:{}", JSON.toJSONString(result));
        return result;
    }

    /**
     * 拦截未知的运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        e.printStackTrace();
        log.error("请求地址'{}',发生未知异常.{}", requestURI, e.getMessage());
        AjaxResult result = AjaxResult.error(e.getMessage());
        result.put("resultCode", "FAIL");
        result.put("resultMessage",e.getMessage());
        log.error("handleRuntimeException:{}", JSON.toJSONString(result));
        return result;
    }

    /**
     * 系统异常
     */
    @ExceptionHandler(DuplicateKeyException.class)
    public AjaxResult duplicateKeyException(Exception e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        AjaxResult result = AjaxResult.error("数据已存在,请不要重复添加");
        result.put("resultCode", "FAIL");
        result.put("resultMessage","数据已存在,请不要重复添加");
        log.error("duplicateKeyException:{}", JSON.toJSONString(result));
        return result;
    }

    /**
     * 系统异常
     */
    @ExceptionHandler(Exception.class)
    public AjaxResult handleException(Exception e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        AjaxResult result = AjaxResult.error(e.getMessage());
        result.put("resultCode", "FAIL");
        result.put("resultMessage", e.getMessage());
        log.error("handleException:{}", JSON.toJSONString(result));
        return result;
    }

    /**
     * 业务异常
     */
    @ExceptionHandler({ServiceException.class})
    public Object handleServiceException(ServiceException e, HttpServletRequest request) {
        log.error(e.getMessage(), e);
        AjaxResult result = AjaxResult.error(e.getMessage());
        result.put("resultCode", "FAIL");
        result.put("resultMessage", e.getMessage());
        log.error("handleServiceException:{}", JSON.toJSONString(result));
        return result;
    }

    /**
     * 无效请求
     */
    @ExceptionHandler({InvalidReqException.class})
    public Object handleInvalidReqException(ServiceException e, HttpServletRequest request) {
        log.error(e.getMessage(), e);
        AjaxResult result = AjaxResult.error(e.getMessage());
        result.put("resultCode", "FAIL");
        result.put("resultMessage", "无效请求");
        log.error("handleInvalidReqException:{}", JSON.toJSONString(result));
        return result;
    }

    /**
     * 参数校验异常
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public Object handleServiceException(MethodArgumentNotValidException e, HttpServletRequest request) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        Map<String, Object> errorMap = fieldErrors.stream()
                .collect(Collectors.toMap(item -> item.getField(), item -> item.getDefaultMessage()));
        AjaxResult result = AjaxResult.error("参数校验失败", errorMap);
        result.put("resultCode", "FAIL");
        result.put("resultMessage", "接口参数验证失败,详细:"+JSON.toJSONString(errorMap));
        log.error("handleServiceException:{}", JSON.toJSONString(result));
        return result;
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public AjaxResult handleBindException(BindException e) {
        log.error(e.getMessage(), e);
        String message = e.getAllErrors().get(0).getDefaultMessage();
        AjaxResult result = AjaxResult.error(message);
        result.put("resultCode", "FAIL");
        result.put("resultMessage", message);
        log.error("handleBindException:{}", JSON.toJSONString(result));
        return result;
    }

    /**
     * 限流异常
     */
    @ExceptionHandler(RateLimiterException.class)
    public Object rateLimiterException(RedirectAttributes redirectAttributes, HttpServletRequest request, HttpServletResponse response, RateLimiterException e) {
        String infoMsg = "当前服务器拥堵,请稍后重试";
        AjaxResult result = AjaxResult.error(infoMsg);
        result.put("resultCode", "FAIL");
        result.put("resultMessage", infoMsg);
        log.error("rateLimiterException:{}", JSON.toJSONString(result));
        return result;

    }

    /**
     * 限流异常
     */
    @ExceptionHandler(ErrorClientException.class)
    public Object errorClientException(RedirectAttributes redirectAttributes, HttpServletRequest request, HttpServletResponse response, ErrorClientException e) throws UnsupportedEncodingException {
        String infoMsg = "非法客户端访问";
        AjaxResult result = AjaxResult.error(infoMsg);
        result.put("resultCode", "FORBIDDEN");
        result.put("resultMessage", infoMsg);
        log.error("errorClientException:{}", JSON.toJSONString(result));
        return result;

    }
}
